//==============================================================================
// Copyright (c) 2008-2013 Niall Ryan. All Rights Reserved.
//==============================================================================

#include <gtest/gtest.h>

#include "hydra/locks/PetersonLock.h"
#include "hydra/locks/FilterLock.h"
#include "hydra/locks/BakeryLock.h"
#include "hydra/locks/TASLock.h"
#include "hydra/locks/TTASLock.h"
#include "hydra/thread/Thread.h"
#include "hydra/locks/ArrayLock.h"
#include "hydra/locks/CLHLock.h"

using namespace Hydra;


template<typename LockType, int NUM_ITERATIONS, int NUM_THREADS>
struct LockTestType
{
    typedef LockType Lock;
    static const int NumIterations = NUM_ITERATIONS;
    static const int NumThreads = NUM_THREADS;
};


template<typename T>
class LockWithIdTest : public ::testing::Test
{
protected:
    static const int NumThreads = T::NumThreads;
    static const int NumIterations = T::NumIterations;

	static void IncFuncStatic(LockWithIdTest* test, int id)		{ test->IncFunc(id); }
	void IncFunc(int id)
	{
		for( int i=0; i<T::NumIterations; ++i )
		{
			m_lock.lock(id);
			++m_counter;
			m_lock.unlock(id);
		}
	}

	typename T::Lock m_lock;
	int m_counter;
};

typedef ::testing::Types<
    LockTestType<PetersonLock, 1000000, 2>,
    LockTestType<FilterLock<5>, 500000, 5>,
    LockTestType<BakeryLock<5>, 500000, 5>
    > LockWithIdTestTypes;
TYPED_TEST_CASE(LockWithIdTest, LockWithIdTestTypes);

TYPED_TEST(LockWithIdTest, Stuff)
{
	//TEST_TIMER;

	m_counter = 0;
	Thread threads[NumThreads];
	for( int i=0; i<NumThreads; ++i )
		threads[i] = Thread(&IncFuncStatic, this, i);
	for( int i=0; i<NumThreads; ++i )
		threads[i].Start();
	for( int i=0; i<NumThreads; ++i )
		threads[i].Join();
	EXPECT_TRUE(m_counter==NumIterations*NumThreads);
}



template<typename T>
class LockTest : public ::testing::Test
{
protected:
    static const int NumThreads = T::NumThreads;
    static const int NumIterations = T::NumIterations;

    static void IncFuncStatic(LockTest* test)		{ test->IncFunc(); }
	void IncFunc()
	{
		for( int i=0; i<NumIterations; ++i )
		{
			m_lock.lock();
			++m_counter;
			m_lock.unlock();
		}
	}

	typename T::Lock m_lock;
	int m_counter;
};

typedef ::testing::Types<
    LockTestType<TASLock, 10000000, 2>,	//light contention
    LockTestType<TTASLock, 10000000, 2>,
    LockTestType<ArrayLock<16>, 10000000, 2>,
    LockTestType<TASLock, 2500000, 4>,
    LockTestType<TTASLock, 2500000, 4>,
    LockTestType<ArrayLock<16>, 2500000, 4>,
    LockTestType<CLHLock, 2500000, 4>
    > LockTestTypes;
TYPED_TEST_CASE(LockTest, LockTestTypes);

TYPED_TEST(LockTest, Stuff)
{
	//TEST_TIMER;

	m_counter = 0;
	Thread threads[NumThreads];
	for( int i=0; i<NumThreads; ++i )
		threads[i] = Thread(&IncFuncStatic, this);
	for( int i=0; i<NumThreads; ++i )
		threads[i].Start();
	for( int i=0; i<NumThreads; ++i )
		threads[i].Join();
	EXPECT_TRUE(m_counter==NumIterations*NumThreads);
}

